#include "sha_sdg.h"

static unsigned char SDG_PADDING[64] = {
  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

static unsigned int SDG_SHA_K[64] = {
	0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
	0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
	0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
	0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
	0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
	0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
	0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
	0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
	0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
	0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
	0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
	0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
	0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
	0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
	0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
	0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
/**********************************************************
*	functions
**********************************************************/

/**************************************************************************
* Function Name  : SDG_SHA_init
* Description    : SHAL256 initialization. Begins an SHA1 operation, writing a new context.
* Input          : None
* Output         : - *context : the point of sha1 context
* Return         : None
**************************************************************************/
void SDG_SHA_init(SDG_SHA_CTX *context)
{
  context->count[0] = context->count[1] = 0;

  context->state[0] = 0x6a09e667;
  context->state[1] = 0xbb67ae85;
  context->state[2] = 0x3c6ef372;
  context->state[3] = 0xa54ff53a;
  context->state[4] = 0x510e527f;
  context->state[5] = 0x9b05688c;
  context->state[6] = 0x1f83d9ab;
  context->state[7] = 0x5be0cd19;
}

/* Encodes input (uint32_t) into output (unsigned char). Assumes len is
  a multiple of 4.
 */
static void SDG_SHA_encode (uint8_t *output, uint32_t *input, uint32_t len)
{
  unsigned int i, j;

  for (i = 0, j = 0; j < len; i++, j += 4) 
  {
	  output[j+3] = (unsigned char)(input[i] & 0xff);
	  output[j+2] = (unsigned char)((input[i] >> 8) & 0xff);
	  output[j+1] = (unsigned char)((input[i] >> 16) & 0xff);
	  output[j] = (unsigned char)((input[i] >> 24) & 0xff);
  }
}
/* Decodes input (unsigned char) into output (uint32_t). Assumes len is
  a multiple of 4.
 */
//static void Decode (uint32_t *output, uint8_t *input, uint32_t len)
//{
  //unsigned int i, j;
//
  //for (i = 0, j = 0; j < len; i++, j += 4)
  //{
	//output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) |
		//(((uint32_t)input[j+2]) << 16) | (((uint32_t)input[j+3]) << 24);
  //}
//}

/* Note: Replace "for loop" with standard memcpy if possible.
 */
static void SDG_SHA_memcpy (uint8_t *output,uint8_t *input, uint32_t len)
{
  unsigned int i;

  for (i = 0; i < len; i++)
  {
	  output[i] = input[i];
  }
}

/* Note: Replace "for loop" with standard memset if possible.
 */
static void SDG_SHA_memset (uint8_t *output, int value, uint32_t len)
{
  unsigned int i;

  for (i = 0; i < len; i++)
  {
	 ((char *)output)[i] = (char)value;
  }
}

/**************************************************************************
* Function Name  : SDG_SHA_transform
* Description    : transform a block(512bit) of message to digest in SHA1 algorithm
* Input          : - *block   : input a block data to be tranformed;
*				 : - *state   : interim state data before transform
* Output		 : - *state   : interim state data after transform
* Return         : None
**************************************************************************/
void SDG_SHA_transform (uint32_t *state, uint8_t *block)
{
	int         t,s;              // Loop counter                
    uint32_t      temp1,temp2;              //Temporary word value        
    uint32_t      W[16];             //Word sequence               
    uint32_t      A, B, C, D, E, F, G, H;     //Word buffers               
	uint8_t 	  flag1,flag2,flag3;
	
    
    // Initialize the first 16 words in the array W     
    for(t = 0; t < 16; t++)
    {
        W[t] = block[t * 4] << 24;
        W[t] |= block[t * 4 + 1] << 16;
        W[t] |= block[t * 4 + 2] << 8;
        W[t] |= block[t * 4 + 3];
    }  

    A = state[0];
    B = state[1];
    C = state[2];
    D = state[3];
    E = state[4];
	F = state[5];
	G = state[6];
	H = state[7];

    for(t = 0; t < 64; t++)
    {
    	s=t&0x0000000F;
    	flag1=(s+14)&0x0000000F;
    	flag2=(s+9)&0x0000000F;
    	flag3=(s+1)&0x0000000F;
    	if(t>=16)
		{
			W[s] = SDG_SHA_A1(W[flag1]) + W[flag2] + SDG_SHA_A0(W[flag3]) + W[s];
		}
        temp1 =  H + SDG_SHA_E1(E) + SDG_SHA_CH(E,F,G) + SDG_SHA_K[t] + W[s];
        temp2 =  SDG_SHA_E0(A) + SDG_SHA_MAJ(A,B,C);
        
		H = G;
		G = F;
		F = E;
		E = D + temp1;
		D = C;
		C = B;
		B = A;
		A = temp1 + temp2;
    }

  
    state[0] += A;
    state[1] += B;
    state[2] += C;
    state[3] += D;
    state[4] += E;
	state[5] += F;
    state[6] += G;
    state[7] += H;
}


/**************************************************************************
* Function Name  : SDG_SHA_update
* Description    : SHAL256 block update operation. Continues an SHA1 message-digest
*				 : operation, processing another message block, and updating the
*				 : context.
* Input          : - *context : context before transform
*				 : - *input   : input message
*                : - inputlen : the byte length of input message
* Output		 : - *context : context after transform
* Return         : None
**************************************************************************/
void SDG_SHA_update (SDG_SHA_CTX *context, uint8_t *input,uint32_t inputLen)
{
  uint32_t i, index, partLen;

  // Compute number of bytes mod 64
  index = (uint32_t)((context->count[1] >> 3) & 0x3F);

  // Update number of bits
  if ((context->count[1] += ((uint32_t)inputLen << 3)) < ((uint32_t)inputLen << 3))
  {
	context->count[0]++;
  }	
  context->count[0] += ((uint32_t)inputLen >> 29);

  partLen = 64 - index;

  // Transform as many times as possible
  if (inputLen >= partLen) {
	 SDG_SHA_memcpy((uint8_t*)&context->buffer[index], (uint8_t*)input, partLen);

	 SDG_SHA_transform (context->state, context->buffer);

	 for (i = partLen; i + 63 < inputLen; i += 64)
	 {
		SDG_SHA_transform (context->state, &input[i]);
	 }	   
	 index = 0;
  }
  else
  {
	 i = 0;
  }
  // Buffer remaining input 
  SDG_SHA_memcpy ((uint8_t*)&context->buffer[index], (uint8_t*)&input[i], inputLen-i);
}


/**************************************************************************
* Function Name  : SDG_SHA_final
* Description    : SHAL256 finalization. Ends an SHAL256 message-digest operation, writing the
*                : the message digest and zeroizing the context.
* Input          : - *context : context before transform
* Output		 : - *digest  : message digest
* Return         : None
**************************************************************************/
void SDG_SHA_final (uint8_t *digest, SDG_SHA_CTX *context)
{
  uint8_t bits[8];
  uint32_t index, padLen;

  // Save number of bits 
  SDG_SHA_encode (bits, context->count, 8);

  // Pad out to 56 mod 64
  index = (uint32_t)((context->count[1] >> 3) & 0x3f);
  padLen = (index < 56) ? (56 - index) : (120 - index);
  SDG_SHA_update (context, SDG_PADDING, padLen);

  // Append length (before padding)
  SDG_SHA_update (context, bits, 8);

  // Store state in digest 
  SDG_SHA_encode (digest, context->state, 32);

  // Zeroize sensitive information
  SDG_SHA_memset ((uint8_t*)context, 0, sizeof (*context));
}

/**************************************************************************
* Function Name  : SDG_SHA_hash
* Description    : transform message to digest in SHA1 algorithm
* Input          : - *pDataIn : input message to be tranformed;
				 : - DataLen  : the byte length of message;
* Output		 : - *pDigest : output the digest;
* Return         : None
**************************************************************************/
void SDG_SHA_hash(uint8_t *pDataIn,uint32_t DataLen,uint8_t *pDigest)
{
	SDG_SHA_CTX context;

	SDG_SHA_init(&context);

	SDG_SHA_update(&context,pDataIn,DataLen);

	SDG_SHA_final(pDigest,&context);
}
